#include "utils.h"
#include "cs2nucl.h"
#include "sortDnaList.h"
#include "assembleDna.h"
#include "histo.h"
#if defined GEN_RANDOM_INPUT
#include "randomGenerator.h"
#endif /* GEN_RANDOM_INPUT */
#include <cstdlib>
#include <fstream>
#include <iostream>
#include <sstream>
#include <vector>

#ifdef _OPENMP
#include <omp.h>
#else
#define omp_get_max_threads()	1
#define omp_get_thread_num()	0
#endif /* _OPENMP */

#define SEQ_LEN	24
#define HISTO_FREQ	1
#define CH_SZ		20
#define OAF		4

using namespace std;

int main(int argc, char* argv[])
{
	if (argc != 2) {
		cout << "Usage: " << argv[0]
			<< " <input-data-file-name>" << endl;
		return EXIT_FAILURE;
	}

	/* Variable declarations */
	string line;
	ifstream in(argv[1]);
	DnaSeq * lstIn, *lstOut;
	size_t lstInLvl, lstOutLvl;


	/* Go to the first line starting with ">" to skip header lines */
	getline(in, line);
	while (line[0] != '>' && !in.eof() && !in.bad()) {
		getline(in, line);
	}

	/* Load lines of input file starting with a "T" */
	vector<string> colorSeq;
	for(;!in.eof() && !in.bad();) {
		if (line[0] == 'T') {
			colorSeq.push_back(line);
		}
		getline(in, line);
	}

	if ( colorSeq.size() == 0) return EXIT_FAILURE;

	/* Print first and last loaded lines for debug */
	cout << colorSeq[0] << endl;
	cout << colorSeq[colorSeq.size()-1] << endl;

	/* Display number of loaded lines */
	cout << colorSeq.size() << " lines loaded\n";

	/* Convert to Nucleotides */
	lstIn = new DnaSeq[OAF*colorSeq.size()];
	lstInLvl=0;
	vector<string>::iterator it;
	// Right now superfluous */
	int lCnt[4]={0,0,0,0};

	for (it=colorSeq.begin(); it < colorSeq.end(); it++) {
		if (color2Nucleotide(*it, lstIn[lstInLvl], SEQ_LEN, lCnt) == 0) {
			/* Convertion successful */
			lstInLvl++;
		}

	}

	cout << lstInLvl << " lines converted successfully\n";
	cout << &lstIn[lstInLvl-1] << endl;

	/* Sort the list and remove double items */

	lstIn = sortDnaList(lstIn, lstInLvl);

#if defined GEN_RANDOM_INPUT
	/* Generate Random List with same characteristics */
	map <int, int> distribution;
	distribution = count(lstInLvl, lstIn);
	unsigned int trimming=SEQ_LEN;
	DnaSeq * randomList = createList(lstInLvl, trimming,
		distribution[0], distribution[1], distribution[2], distribution[3]);
	lstIn = randomList;
#endif /* GEN_RANDOM_INPUT */

	cout << lstInLvl << endl;
	cout << &lstIn[lstInLvl-1] << endl;

	/* Create output list */
	lstOut = new DnaSeq[OAF*lstInLvl];
	lstOutLvl=0;
	Histogram tmp;

	/* Try to assemble sequences */

	for (int k=0; k<100; k++)
	{
		{
#pragma omp parallel \
		default(none) shared(lstOut, lstOutLvl) firstprivate(lstIn, lstInLvl, k)
			{
				int idx=omp_get_thread_num();
				DnaSeq seq;
#pragma omp for schedule(dynamic, CH_SZ)
			for (int i=0; i < lstInLvl; i++) {
				for (int j=i+1; j < lstInLvl; j++) {
					if (assembleDna4(&lstIn[i], &lstIn[j],
							&seq, SEQ_LEN-1+k) == 1)
#pragma omp critical
					{
						mergeLists(lstOut, lstOutLvl, &seq, 1);
					}
				}
			}}
		} /* End parallel region */

		/* Get not used sequences into output list */
		mergeLists(lstOut, lstOutLvl, lstIn, lstInLvl);

		if (k==0 || k % HISTO_FREQ == (HISTO_FREQ - 1) )
		{
			Histogram H(lstOut, lstOutLvl);
			H.writeData("../data/data" + to_string(k));
			if (H == tmp)
				break;
			tmp = H;
		}

		/* Swap lists */
		DnaSeq *tmpLst = lstIn;
		lstIn = lstOut;
		lstInLvl = lstOutLvl;
		lstOut = tmpLst;
		lstOutLvl = 0;
	}



	delete[] lstIn;
	delete[] lstOut;

	in.close();

	return EXIT_SUCCESS;

}
